package com.xxlie.auth.security.auth.jwt;

import com.xxlie.auth.security.auth.JwtAuthenticationToken;
import com.xxlie.auth.common.exception.LBLHandlerException;
import com.xxlie.auth.common.exception.enums.SecurityExceptionEnums;
import com.xxlie.auth.domain.User;
import com.xxlie.auth.security.WebSecurityUtils;
import com.xxlie.auth.security.config.JwtSettings;
import com.xxlie.auth.security.model.UserContext;
import com.xxlie.auth.security.model.token.RawAccessJwtToken;
import com.xxlie.auth.service.UserService;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * JwtAuthenticationProvider
 *
 * @author xxlie.com
 * @since 2017/7/30
 */
@Component
@SuppressWarnings("unchecked")
public class JwtAuthenticationProvider implements AuthenticationProvider {

    private final UserService userService;
    private final JwtSettings jwtSettings;

    @Autowired
    public JwtAuthenticationProvider(JwtSettings jwtSettings, UserService userService) {
        this.jwtSettings = jwtSettings;
        this.userService = userService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {

        RawAccessJwtToken rawAccessToken = (RawAccessJwtToken) authentication.getCredentials();

        Jws<Claims> jwsClaims = rawAccessToken.parseClaims(jwtSettings.getTokenSigningKey());
        String subject = jwsClaims.getBody().getSubject();

        Optional<User> userOptional = userService.getByUsername(subject);
        if (!userOptional.isPresent()) {
            throw new LBLHandlerException(SecurityExceptionEnums.UNAUTHORIZED_FAILED);
        }

        List<String> scopes = jwsClaims.getBody().get("scopes", List.class);
        List<GrantedAuthority> authorities = scopes.stream()
                .map(authority -> new SimpleGrantedAuthority(authority))
                .collect(Collectors.toList());

        String algorithm = jwsClaims.getBody().get(WebSecurityUtils.X_ALGORITHM, String.class);
        String secret = jwsClaims.getBody().get(WebSecurityUtils.X_SECRET, String.class);
        String body = rawAccessToken.getBody();
        String digestClient = rawAccessToken.getDigestClient();

//        String digestServer = JwtSigner.encodeMac(secret, body, algorithm);
//        if (StringUtils.isEmpty(digestServer) || digestServer.equals(digestClient)) {
//            throw new SignInvalidException("Signature verification failed");
//        }

        UserContext context = UserContext.create(subject, authorities);

        return new JwtAuthenticationToken(context, context.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (JwtAuthenticationToken.class.isAssignableFrom(authentication));
    }

}
